#include "stdlib.hpp"

#include <purewell/pairfile.hpp>

#include "libfile.hpp"

namespace purewell
{

#define _get_fd(idx) (idx == 0?m_fd:m_fd2)

PWPairFile::PWPairFile() : PWFile(), m_fd2(-1)
{
}

PWPairFile::~PWPairFile(void)
{
	int fd[2];
	detach2(fd);

	if ( -1 != fd[0] )
	{
		::close(fd[0]);
	}

	if ( -1 != fd[1] )
	{
		::close(fd[1]);
	}
}

bool
PWPairFile::attach2(const int fd[2])
{
	m_fd = fd[0];
	m_fd2 = fd[1];
	return true;
}

void
PWPairFile::detach2(int* fd)
{
	if ( fd )
	{
		fd[0] = m_fd;
		fd[1] = m_fd2;
	}

	m_fd = -1;
	m_fd2 = -1;
}

bool
PWPairFile::attach(int fd, size_t idx)
{
	if ( 0 == idx )
	{
		m_fd = fd;
	}
	else
	{
		m_fd2 = fd;
	}

	return true;
}

int
PWPairFile::detach(int fd, size_t idx)
{
	int res(-1);
	if ( 0 == idx )
	{
		res = m_fd;
		m_fd = fd;
	}
	else
	{
		res = m_fd2;
		m_fd2 = fd;
	}

	return res;
}

bool
PWPairFile::attach(int fd)
{
	_PWTRACE("WARNING! PWPairFile's using PWFile::attach"
		" is dangerous!","");
	return PWFile::attach(fd);
}

int
PWPairFile::detach(void)
{
	_PWTRACE("WARNING! PWPairFile's using PWFile::detach"
		" is dangerous!","");
	return PWFile::detach();
}

void
PWPairFile::destroy(void)
{
	int fd[2];
	detach2(fd);

	if ( -1 != fd[0] ) ::close(fd[0]);
	if ( -1 != fd[1] ) ::close(fd[1]);
}

ssize_t
PWPairFile::read(void* buf, size_t blen, size_t idx)
{
	return ::read(_get_fd(idx), (char*)buf, blen);
}

ssize_t
PWPairFile::write(const void* buf, size_t blen, size_t idx)
{
	return ::write(_get_fd(idx), (const char*)buf, blen);
}

ssize_t
PWPairFile::readParallel(void* buf, size_t blen, off_t offset, size_t idx)
{
#if (_XOPEN_SOURCE - 0 ) >= 500
	return ::pread(_get_fd(idx), (char*)buf, blen, offset);
#else
	__PWERRNO_NOIMPL();
	return -1;
#endif
}

ssize_t
PWPairFile::writeParallel(const void* buf, size_t blen, off_t offset,
	size_t idx)
{
#if (_XOPEN_SOURCE - 0 ) >= 500
	return ::pwrite(_get_fd(idx), (const char*)buf, blen, offset);
#else
	__PWERRNO_NOIMPL();
	return -1;
#endif
}

bool
PWPairFile::setModeBlock(bool block, size_t idx)
{
#ifndef WIN32
	return __PWSetFlags(!block, _get_fd(idx), O_NONBLOCK);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::getModeBlock(bool& block, size_t idx) const
{
#ifndef WIN32
	if ( !__PWGetFlags(block, _get_fd(idx), O_NONBLOCK) )
	{
		return false;
	}

	block = !block;
	return true;
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::setModeAsync(bool async, size_t idx)
{
#ifndef WIN32
	return __PWSetFlags(async, _get_fd(idx), O_ASYNC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::getModeAsync(bool& async, size_t idx) const
{
#ifndef WIN32
	return __PWGetFlags(async, _get_fd(idx), O_ASYNC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::setModeCloseOnExec(bool coe, size_t idx)
{
#ifndef WIN32
	return __PWSetFDFlags(coe, _get_fd(idx), FD_CLOEXEC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::getModeCloseOnExec(bool& coe, size_t idx) const
{
#ifndef WIN32
	return __PWGetFDFlags(coe, _get_fd(idx), FD_CLOEXEC);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::getMode(int& mode, size_t idx) const
{
#ifndef WIN32
	int res(fcntl(_get_fd(idx), F_GETFL));
	if ( -1 == res )
	{
		return false;
	}

	mode = res;
	return true;
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::setMode(int mode, size_t idx)
{
#ifndef WIN32
	return 0 == fcntl(_get_fd(idx), F_SETFL, mode);
#else
	__PWERRNO_NOIMPL();
	return false;
#endif
}

bool
PWPairFile::duplicate(int srcfd, size_t idx)
{
	int dfd(dup(srcfd));
	if ( -1 == dfd ) return false;

	bool res(attach(dfd, idx));
	if ( false == res )
	{
		::close(dfd);
	}

	return res;
}

bool
PWPairFile::duplicate(int srcfd, int newfd, size_t idx)
{
	int dfd(dup2(srcfd, newfd));
	if ( -1 == dfd ) return false;

	bool res(attach(dfd, idx));
	if ( false == res )
	{
		::close(dfd);
	}

	return res;
}

};//namespace purewell

